package node

import (
	"container/list"
	"fmt"
	"math"
	"math/rand"
	"packet"
	"strconv"
	"time"
)

type Node struct {
	Value      int
	Ch         chan packet.Packet
	Position   int
	RadioRange int
}

const NumNodi = 5

func (this Node) Start(nodes [NumNodi]Node) {
	var arrivedPackets list.List

	go this.Generate(nodes)

	for {
		var p packet.Packet
		p = <-this.Ch
		fmt.Println("[" + strconv.Itoa(this.Value) + "] Pack" + strconv.Itoa(p.Value) + " Arrivato. FROM: " + strconv.Itoa(p.From) + " TO: " + strconv.Itoa(p.To))

		var toMe, toRoute, isReply, done bool = false, false, false, false

		if p.To == this.Value {
			toMe = true
			toRoute = false
		} else {
			toMe = false
			toRoute = true
		}
		if p.IsIn(arrivedPackets) {
			done = true
		}
		if p.Reply {
			isReply = true
		}

		if done {
			//IGNORE
			fmt.Println("[" + strconv.Itoa(this.Value) + "] Pack "+ strconv.Itoa(p.Value) + " Ignored")
			continue
		}

		if toRoute {
			if isReply {
				//SEND BOTTOM UP
				dest := (p.Hops.Remove(p.Hops.Back())).(int)
				nodes[dest].Ch <- p
			} else {
				//SEND BROADCAST RADIORANGE
				sender := p.Hops.Back()
				p.Hops.PushBack(this.Value)

				inoltrato := false
				for i, _ := range nodes { //tra tutti i nodi
					if this.Position != nodes[i].Position { //tranne me stesso
						if sender.Value != nodes[i].Value { //tranne il mittente
							if this.Distance(nodes[i]) < this.RadioRange { //purchè sia all'interno del radioRange
								inoltrato = true
								fmt.Println("[" + strconv.Itoa(this.Value) + "] Pack" + strconv.Itoa(p.Value) + " Inoltro a " + strconv.Itoa(nodes[i].Value))
								nodes[i].Ch <- p
							} else {
								fmt.Println("[" + strconv.Itoa(this.Value) + "] Pack" + strconv.Itoa(p.Value) + " Impossibile inoltrare a " + strconv.Itoa(nodes[i].Value))
							}
						}
					}
				}

				if !inoltrato {
					fmt.Println("[" + strconv.Itoa(this.Value) + "] Pack" + strconv.Itoa(p.Value) + " Non inoltrabile da qui")
				}
			}
			continue
		}

		if toMe {
			if isReply {
				//OK RETURNED HOME
				fmt.Println("[" + strconv.Itoa(this.Value) + "] Pack" + strconv.Itoa(p.Value) + " Reply returned home")
			} else {
				//SEND REPLY
				fmt.Println("[" + strconv.Itoa(this.Value) + "] Pack" + strconv.Itoa(p.Value) + " Arrivato a destinazione e invio Reply")
				var pr packet.Packet
				pr.From = p.To
				pr.To = p.From
				pr.Value = rand.Int()
				pr.Reply = true
				pr.Path = p.Hops
				dest := (p.Hops.Remove(p.Hops.Back())).(int)
				nodes[dest].Ch <- pr
			}
			continue
		}
	}
}

func isIn(i int, l list.List) bool {
	for e := l.Front(); e != nil; e = e.Next() {
		if i == e.Value {
			return true
		}
	}
	return false
}

func (this Node) Distance(a Node) int {
	return int(math.Abs(float64(this.Position - a.Position)))
}

func (this Node) Generate(nodes [NumNodi]Node) {
	i := 0
	for {
		t := time.Duration(rand.Intn(10) + 5)
		time.Sleep(time.Second * t)

		nonce := rand.Int()

		var p packet.Packet
		p.Value = nonce
		p.To = rand.Intn(NumNodi)
		p.From = this.Value
		p.Hops.PushBack(this.Value)

		nodes[rand.Intn(NumNodi)].Ch <- p
		i++
		if i >= 1 {
			return
		}
	}
	return
}
